import Ou_log
import time
from typing import List

import allure
from appium.webdriver import WebElement
from appium.webdriver.common.appiumby import AppiumBy
from appium.webdriver.webdriver import WebDriver
from selenium.common import NoSuchElementException
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.wait import WebDriverWait

from utils.Loguru import Ou_log
from utils.file_util import get_path


class BasePage:
    """
    封装基类
    """

    def __init__(self, driver: WebDriver = None):
        self.driver = driver

    def swipe_find(self, loc, max_num=5, reserve=False):
        """
        滑动页面查找元素，若元素未找到，则滑动页面
        :param loc: 元素定位方式 (By.ID,'#id')
        :param max_num: 最大滑动次数，默认5次
        :param reserve: 是否反向滑动，默认下滑。若为True，则上滑
        :return: 元素对象
        """
        # 减少隐式等待时间，便于快速滑动页面
        self.driver.implicitly_wait(1)
        # 循环滑动页面
        for i in range(max_num):
            try:
                # 尝试定位元素
                ele = self.driver.find_element(*loc)
                log_text = f"元素定位：定位方式为{loc[0]},定位表达式为{loc[1]}"
                Ou_log.info(log_text)

                # 若定位成功，则恢复隐式等待时间，并返回元素对象
                self.driver.implicitly_wait(10)
                return ele

            except Exception:
                # 定位元素失败，执行滑动操作
                Ou_log.info(f"没有查到元素，开始第{i + 1}次滑动！")
                self.swipe_window(reserve)

        # 完成滑动，仍查不到元素，则恢复隐式等待时间，并抛出异常
        self.driver.implicitly_wait(10)
        raise NoSuchElementException(f"滑动{max_num}次，未查到元素\n定位方式为{loc[0]},定位表达式为{loc[1]}")

    def swipe_window(self, reserve=False):
        """
        滑动界面
        :param reserve: 是否反向滑动，默认下滑。若为True，则上滑
        :return:
        """
        # 获取设备尺寸
        size = self.driver.get_window_size()
        # 获取页面长度和宽度
        height = size.get("height")
        width = size.get("width")
        # 设定滑动页面的坐标
        start_x = width / 2
        start_y = height * 0.8
        end_x = start_x
        end_y = height * 0.2
        # 执行滑动操作(滑动2秒)
        if reserve:
            # 上滑
            self.driver.swipe(start_x, end_y, end_x, start_y, 2000)
        else:
            # 下滑
            self.driver.swipe(start_x, start_y, end_x, end_y, 2000)

    def wait_until(self, method, timeout=10):
        """
        封装显示等待
        :param method: 显示等待方法
        :param timeout: 超时时间
        :return: 元素对象
        """
        ele: WebElement = WebDriverWait(self.driver, timeout).until(method)
        return ele

    @allure.step("获取toast提示")
    def get_toast_text(self):
        """
        获取 toast 文本
        :return: str
        """
        return self.find_ele((AppiumBy.XPATH, "//*[@class='android.widget.Toast']")).text

    def wait_ele_clickable(self, loc, timeout=10):
        """
        等待元素可以被点击
        :param loc: 元素定位方式 (By.ID,'#id')
        :param timeout: 超时时间
        :return: 元素对象
        """
        ele: WebElement = self.wait_until(ec.element_to_be_clickable(loc), timeout)
        return ele

    def wait_ele_visible(self, loc, timeout=10):
        """
        等待元素可以被看见
        :param loc: 元素定位方式 (By.ID,'#id')
        :param timeout: 超时时间
        :return: 元素对象
        """
        ele: WebElement = self.wait_until(ec.visibility_of_element_located(loc), timeout)
        return ele

    def _muliti_click(self, button_ele, until_ele):
        """
        excepted_condition: 多次点击元素，直到指定元素出现
        :param button_ele: 待点击的元素
        :param until_ele: 等待出现的元素
        :return: 点击方法
        """

        def inner(driver):
            # 封装点击方法
            driver.find_element(*button_ele).click()
            return driver.find_element(*until_ele)

        return inner

    def click_until_ele_located(self, button_ele, until_ele, timeout=10):
        """
        多次点击元素，直到指定元素出现
        :param button_ele: 待点击的元素
        :param until_ele: 等待出现的元素
        :param timeout: 超时时间
        :return: 待出现的元素对象
        """
        ele: WebElement = self.wait_until(self._muliti_click(button_ele, until_ele), timeout)
        return ele

    def go_back(self, num):
        """
        返回上一级
        :param num: 次数
        :return:
        """
        Ou_log.info(f"返回{num}次")
        for _ in range(num):
            self.driver.back()

        return self

    def screenshot(self):
        """
        截图
        :return: 图片保存路径
        """
        # 截图命名
        now_time = time.strftime('%Y_%m_%d_%H_%M_%S')
        image_name = f"{now_time}.png"
        # 拼接截图保存路径
        # windows f"{self.get_path('image')}\\{image_name}"
        image_path = f"{get_path('image')}/{image_name}"
        Ou_log.info(f"截图保存路径为 {image_path}")
        # 截图
        self.driver.save_screenshot(image_path)
        # 添加截图到 allure
        allure.attach.file(image_path, name="查找元素异常截图",
                           attachment_type=allure.attachment_type.PNG)

    def save_page_source(self):
        """
        保存页面源码
        :return: 页面源码保存路径
        """
        # 文件命名
        now_time = time.strftime('%Y_%m_%d_%H_%M_%S')
        pagesource_name = f"{now_time}_pagesource.html"
        # 拼接文件保存路径
        # windows f"{self.get_path('pagesource')}\\{pagesource_name}"
        pagesource_path = f"{get_path('page_source')}/{pagesource_name}"
        Ou_log.info(f"页面源码文件保存路径为 {pagesource_path}")

        # 保存 page source
        with open(pagesource_path, "w", encoding="utf-8") as f:
            f.write(self.driver.page_source)

        # pagesource 添加到 allure 报告
        allure.attach.file(pagesource_path, name="查找元素异常的页面源码",
                           attachment_type=allure.attachment_type.TEXT)
